#pragma once
#include <sstream>
#include <ctime>
#include "data.hpp"
#include "httplib.h"
#include "config.hpp"
// 下载及上传目录的创建
extern cloud::DataManager *data;

namespace cloud
{
    class Service
    {
    public:
        Service()
        {
            _server_prot = Config::getInstance().getServerPort();
            _server_ip = Config::getInstance().getServerIp();
            _download_prefix = Config::getInstance().getDownloadPrefix();
        }

        void runModule()
        {
            _server.Post("/upload", upLoad);
            _server.Get("/listshow", listShow);
            _server.Get("/", listShow);
            std::string down_url = _download_prefix + "(.*)";
            _server.Get(down_url, downLoad);
            _server.listen(_server_ip.c_str(), _server_prot); 
        }
    private:

        static void upLoad(const httplib::Request &req, httplib::Response &rsp)
        {
            auto ret = req.has_file("file");
            if(ret == false)
            {
                rsp.status = 400;
                return;
            }

            const auto &file = req.get_file_value("file");
            std::string back_dir = Config::getInstance().getBackDir();
            std::string real_path = back_dir + UtilFile(file.filename).fileName();
            // 写入数据
            UtilFile back_file(real_path);
            back_file.setContent(file.content);
            // 保存文件数据
            BackupInfo info(real_path);
            data->insert(info);
        }

        static void listShow(const httplib::Request &req, httplib::Response &rsp)
        {
            // 获取所有的文件备份信息
            std::vector<BackupInfo> infos;
            data->getAll(&infos);
            // 根据备份信息，组织HTML文件
            std::stringstream ss;
            ss << "<html><head><title>Download</title></head>";
            ss << "<body><h1>Download</h1><table>";
            for(auto &info: infos)
            {
                ss << "<tr>";
                std::string filename = UtilFile(info.real_path).fileName();
                ss << "<td><a href='" << info.url << "'>" << filename << "</a></td>";
                ss << "<td align='right'>" << ctime(&info.mtime) << "</td>";
                ss << "<td align='right'>" << info.fsize / 1024 << "K</td>";
                ss << "</tr>";
            }
            ss << "</table></body></html>";
            rsp.body = ss.str();
            rsp.set_header("Content-Type", "text/html");
            rsp.status = 200;
        }

        static std::string getEtag(const BackupInfo &info)
        {
            std::string etag = UtilFile(info.real_path).fileName();
            etag += '-';
            etag += std::to_string(info.fsize);
            etag += '-';
            etag += std::to_string(info.mtime);
            return etag;
        }

        static void downLoad(const httplib::Request &req, httplib::Response &rsp)
        {
            BackupInfo info;
            // 获取文件备份信息
            data->getOneByUrl(req.path, &info);
            // 如果文件已经被压缩，则解压文件，删除压缩包，及更新备份信息
            if(info.pack_flag == true)
            {
                UtilFile uf(info.pack_path);
                uf.uncompress(info.real_path);
                uf.removeFile();
                info.pack_flag = false;
                data->update(info);
            }

            bool resume = false;
            std::string old_Etag;
            if(req.has_header("If-Range"))
            {
                old_Etag = req.get_header_value("If-Range");
                // 如果有"If-Range"该字段且和目前备份信息字段相同，则符合断点续传的条件
                if(old_Etag == getEtag(info))
                    resume = true;
            }

            UtilFile uf(info.real_path);
            // httplib库内部自己实现了对断点续传的处理
            // 只需要我们自己将数据写入到body中，内部会根据根据请求区间自行截取目标数据区间
            uf.getContent(rsp.body);
            rsp.set_header("Accept-Ranges", "bytes");
            rsp.set_header("Etag", getEtag(info));
            rsp.set_header("Content-Type", "application/octet-stream");
            if(resume == true)
            {
                rsp.status = 206;
            }
            else
            {
                rsp.status = 200;
            }
        }
    private:
        int _server_prot;
        std::string _server_ip;
        std::string _download_prefix;
        httplib::Server _server;
    };
}